package com.xiaozhu.acsp;

import com.xiaozhu.anno.AutoFill; // 自定义注解，用于标记需要自动填充的地方
import com.xiaozhu.enum_xz.MyEnum; // 枚举类，定义了 INSERT、UPDATE 等操作类型
import com.xiaozhu.utils.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @author 朱慧军
 * @version 1.0
 *
 * 切面类（AOP）
 * 功能：在执行带有 @AutoFill 注解的方法时，
 *      自动给参数对象填充创建时间、修改时间等字段
 */
@Aspect // 声明该类是一个切面
@Component // 注入 Spring 容器
@Slf4j // 日志工具
public class MyAdvice01 {

    /**
     * 前置通知：
     * 在所有标注了 @AutoFill 注解的方法执行之前执行
     */
    @Before("@annotation(com.xiaozhu.anno.AutoFill)")
    public void beforeAdvice(JoinPoint joinPoint) throws Throwable {

        // 1. 获取当前执行的方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        log.info("该方法的对象：{}", method.getName());

        // 2. 获取方法上的 @AutoFill 注解
        AutoFill annotation = method.getAnnotation(AutoFill.class);

        // 获取注解中定义的操作类型（INSERT 或 UPDATE）
        MyEnum value = annotation.operation();
        log.info("该方法上的注解内容：{}", value);

        // 3. 获取方法参数（假设只需要处理第一个参数对象）
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            log.warn("该方法是没有参数的");
            return;
        }

        Object arg = args[0];
        log.info("对象是：{}", arg);

        // 4. 根据操作类型自动填充字段
        if (value == MyEnum.INSERT) {
            // 插入时，既要填充创建时间，又要填充修改时间
            reflectionSetter(arg, "setCreateTime", LocalDateTime.class, LocalDateTime.now());
            reflectionSetter(arg, "setUpdateTime", LocalDateTime.class, LocalDateTime.now());
        } else if (value == MyEnum.UPDATE) {
            // 更新时，只修改更新时间
            reflectionSetter(arg, "setUpdateTime", LocalDateTime.class, LocalDateTime.now());
            reflectionSetter(arg, "setUpdateUser", Long.class, BaseContext.getThreadId());
        }
    }

    /**
     * 使用反射调用对象的 setter 方法，给属性赋值
     *
     * @param object        目标对象（比如 Orders、User 等实体类）
     * @param methodName    setter 方法名称（如 "setCreateTime"）
     * @param parameterType 方法参数的类型（如 LocalDateTime.class）
     * @param parameter     方法参数的值（如 LocalDateTime.now()）
     */
    private void reflectionSetter(Object object,
                                  String methodName,
                                  Class<?> parameterType,
                                  Object parameter) {
        try {

            Class<?> aClass = object.getClass(); // 获取我们类对象。蓝图
            // 1. 根据方法名和参数类型获取目标方法（避免方法重载时找错）
            Method method = aClass.getMethod(methodName, parameterType);

            // 2. 暴力反射，防止方法是 private/protected
            method.setAccessible(true);

            // 3. 执行 setter 方法，把值赋到对象上
            method.invoke(object, parameter);

        } catch (Exception e) {
            log.error("获取方法对象失败~~~~~");
            throw new RuntimeException(e);
        }
    }
}
